/*******************************************************************************
  SERCOM Universal Synchronous/Asynchrnous Receiver/Transmitter PLIB

  Company
    Microchip Technology Inc.

  File Name
    plib_sercom4_usart.c

  Summary
    USART peripheral library interface.

  Description
    This file defines the interface to the USART peripheral library. This
    library provides access to and control of the associated peripheral
    instance.

  Remarks:
    None.
*******************************************************************************/

/*******************************************************************************
* Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries.
*
* Subject to your compliance with these terms, you may use Microchip software
* and any derivatives exclusively with Microchip products. It is your
* responsibility to comply with third party license terms applicable to your
* use of third party software (including open source software) that may
* accompany Microchip software.
*
* THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, WHETHER
* EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, INCLUDING ANY IMPLIED
* WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, AND FITNESS FOR A
* PARTICULAR PURPOSE.
*
* IN NO EVENT WILL MICROCHIP BE LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE,
* INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND
* WHATSOEVER RELATED TO THE SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS
* BEEN ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE
* FULLEST EXTENT ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN
* ANY WAY RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
* THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
*******************************************************************************/

// *****************************************************************************
// *****************************************************************************
// Section: Included Files
// *****************************************************************************
// *****************************************************************************
#include "interrupts.h"
#include "plib_sercom4_usart.h"
// *****************************************************************************
// *****************************************************************************
// Section: Global Data
// *****************************************************************************
// *****************************************************************************
/* SERCOM4 USART baud value for 115200 Hz baud rate */
#define SERCOM4_USART_INT_BAUD_VALUE            (62180UL)



// *****************************************************************************
// *****************************************************************************
// Section: SERCOM4 USART Interface Routines
// *****************************************************************************
// *****************************************************************************

static void SERCOM4_USART_ErrorClear( void )
{
    uint8_t  u8dummyData = 0U;
    USART_ERROR errorStatus = (USART_ERROR) (SERCOM4_REGS->USART.SERCOM_STATUS & (uint16_t)(SERCOM_USART_STATUS_PERR_Msk | SERCOM_USART_STATUS_FERR_Msk | SERCOM_USART_STATUS_BUFOVF_Msk ));

    if(errorStatus != USART_ERROR_NONE)
    {
        /* Clear error flag */
        SERCOM4_REGS->USART.SERCOM_INTFLAG = (uint8_t)SERCOM_USART_INTFLAG_ERROR_Msk;
        /* Clear all errors */
        SERCOM4_REGS->USART.SERCOM_STATUS = (uint16_t)(SERCOM_USART_STATUS_PERR_Msk | SERCOM_USART_STATUS_FERR_Msk | SERCOM_USART_STATUS_BUFOVF_Msk);

        /* Flush existing error bytes from the RX FIFO */
        while((SERCOM4_REGS->USART.SERCOM_INTFLAG & (uint8_t)SERCOM_USART_INTFLAG_RXC_Msk) == (uint8_t)SERCOM_USART_INTFLAG_RXC_Msk)
        {
            u8dummyData = (uint8_t)SERCOM4_REGS->USART.SERCOM_DATA;
        }
    }

    /* Ignore the warning */
    (void)u8dummyData;
}

void SERCOM4_USART_Initialize( void )
{
    /*
     * Configures USART Clock Mode
     * Configures TXPO and RXPO
     * Configures Data Order
     * Configures Standby Mode
     * Configures Sampling rate
     * Configures IBON
     */

    SERCOM4_REGS->USART.SERCOM_CTRLA = SERCOM_USART_CTRLA_MODE_INTCLK | SERCOM_USART_CTRLA_RXPO(0x1UL) | SERCOM_USART_CTRLA_TXPO(0x0UL) | SERCOM_USART_CTRLA_DORD_Msk | SERCOM_USART_CTRLA_IBON_Msk | SERCOM_USART_CTRLA_FORM(0x0UL) | SERCOM_USART_CTRLA_SAMPR(0UL) ;

    /* Configure Baud Rate */
    SERCOM4_REGS->USART.SERCOM_BAUD = (uint16_t)SERCOM_USART_BAUD_BAUD(SERCOM4_USART_INT_BAUD_VALUE);

    /*
     * Configures RXEN
     * Configures TXEN
     * Configures CHSIZE
     * Configures Parity
     * Configures Stop bits
     */
    SERCOM4_REGS->USART.SERCOM_CTRLB = SERCOM_USART_CTRLB_CHSIZE_8BITS | SERCOM_USART_CTRLB_SBMODE_ONE | SERCOM_USART_CTRLB_RXEN_Msk | SERCOM_USART_CTRLB_TXEN_Msk;

    /* Wait for sync */
    while((SERCOM4_REGS->USART.SERCOM_SYNCBUSY) != 0U)
    {
        /* Do nothing */
    }


    /* Enable the UART after the configurations */
    SERCOM4_REGS->USART.SERCOM_CTRLA |= SERCOM_USART_CTRLA_ENABLE_Msk;

    /* Wait for sync */
    while((SERCOM4_REGS->USART.SERCOM_SYNCBUSY) != 0U)
    {
        /* Do nothing */
    }
}





uint32_t SERCOM4_USART_FrequencyGet( void )
{
    return 36000000UL;
}

bool SERCOM4_USART_SerialSetup( USART_SERIAL_SETUP * serialSetup, uint32_t clkFrequency )
{
    bool setupStatus       = false;
    uint32_t baudValue     = 0U;
    uint32_t sampleRate    = 0U;
    uint32_t sampleCount   = 0U;

    if((serialSetup != NULL) && (serialSetup->baudRate != 0U))
    {
        if(clkFrequency == 0U)
        {
            clkFrequency = SERCOM4_USART_FrequencyGet();
        }

        if(clkFrequency >= (16U * serialSetup->baudRate))
        {
            sampleRate = 0U;
            sampleCount = 16U;
        }
        else if(clkFrequency >= (8U * serialSetup->baudRate))
        {
            sampleRate = 2U;
            sampleCount = 8U;
        }
        else if(clkFrequency >= (3U * serialSetup->baudRate))
        {
            sampleRate = 4U;
            sampleCount = 3U;
        }
        else
        {
            /* Do nothing */
        }
        baudValue = 65536U - (uint32_t)(((uint64_t)65536U * sampleCount * serialSetup->baudRate) / clkFrequency);
        /* Disable the USART before configurations */
        SERCOM4_REGS->USART.SERCOM_CTRLA &= ~SERCOM_USART_CTRLA_ENABLE_Msk;

        /* Wait for sync */
        while((SERCOM4_REGS->USART.SERCOM_SYNCBUSY) != 0U)
        {
            /* Do nothing */
        }

        /* Configure Baud Rate */
        SERCOM4_REGS->USART.SERCOM_BAUD = (uint16_t)SERCOM_USART_BAUD_BAUD(baudValue);

        /* Configure Parity Options */
        if(serialSetup->parity == USART_PARITY_NONE)
        {
            SERCOM4_REGS->USART.SERCOM_CTRLA =  (SERCOM4_REGS->USART.SERCOM_CTRLA & ~(SERCOM_USART_CTRLA_SAMPR_Msk | SERCOM_USART_CTRLA_FORM_Msk)) | SERCOM_USART_CTRLA_FORM(0x0UL) | SERCOM_USART_CTRLA_SAMPR((uint32_t)sampleRate); 
            SERCOM4_REGS->USART.SERCOM_CTRLB = (SERCOM4_REGS->USART.SERCOM_CTRLB & ~(SERCOM_USART_CTRLB_CHSIZE_Msk | SERCOM_USART_CTRLB_SBMODE_Msk)) | ((uint32_t) serialSetup->dataWidth | (uint32_t) serialSetup->stopBits);
        }
        else
        {
            SERCOM4_REGS->USART.SERCOM_CTRLA =  (SERCOM4_REGS->USART.SERCOM_CTRLA & ~(SERCOM_USART_CTRLA_SAMPR_Msk | SERCOM_USART_CTRLA_FORM_Msk)) | SERCOM_USART_CTRLA_FORM(0x1UL) | SERCOM_USART_CTRLA_SAMPR((uint32_t)sampleRate); 
            SERCOM4_REGS->USART.SERCOM_CTRLB = (SERCOM4_REGS->USART.SERCOM_CTRLB & ~(SERCOM_USART_CTRLB_CHSIZE_Msk | SERCOM_USART_CTRLB_SBMODE_Msk | SERCOM_USART_CTRLB_PMODE_Msk)) | (uint32_t) serialSetup->dataWidth | (uint32_t) serialSetup->stopBits | (uint32_t) serialSetup->parity ;
        }

        /* Wait for sync */
        while((SERCOM4_REGS->USART.SERCOM_SYNCBUSY) != 0U)
        {
            /* Do nothing */
        }

        /* Enable the USART after the configurations */
        SERCOM4_REGS->USART.SERCOM_CTRLA |= SERCOM_USART_CTRLA_ENABLE_Msk;

        /* Wait for sync */
        while((SERCOM4_REGS->USART.SERCOM_SYNCBUSY) != 0U)
        {
            /* Do nothing */
        }

        setupStatus = true;
    }

    return setupStatus;
}

USART_ERROR SERCOM4_USART_ErrorGet( void )
{
    USART_ERROR errorStatus = (USART_ERROR) (SERCOM4_REGS->USART.SERCOM_STATUS & (uint16_t)(SERCOM_USART_STATUS_PERR_Msk | SERCOM_USART_STATUS_FERR_Msk | SERCOM_USART_STATUS_BUFOVF_Msk ));

    if(errorStatus != USART_ERROR_NONE)
    {
        SERCOM4_USART_ErrorClear();
    }

    return errorStatus;
}

void SERCOM4_USART_Enable( void )
{
    if((SERCOM4_REGS->USART.SERCOM_CTRLA & SERCOM_USART_CTRLA_ENABLE_Msk) == 0U)
    {
        SERCOM4_REGS->USART.SERCOM_CTRLA |= SERCOM_USART_CTRLA_ENABLE_Msk;

        /* Wait for sync */
        while((SERCOM4_REGS->USART.SERCOM_SYNCBUSY) != 0U)
        {
            /* Do nothing */
        }
    }
}

void SERCOM4_USART_Disable( void )
{
    if((SERCOM4_REGS->USART.SERCOM_CTRLA & SERCOM_USART_CTRLA_ENABLE_Msk) != 0U)
    {
        SERCOM4_REGS->USART.SERCOM_CTRLA &= ~SERCOM_USART_CTRLA_ENABLE_Msk;

        /* Wait for sync */
        while((SERCOM4_REGS->USART.SERCOM_SYNCBUSY) != 0U)
        {
            /* Do nothing */
        }
    }
}


void SERCOM4_USART_TransmitterEnable( void )
{
    SERCOM4_REGS->USART.SERCOM_CTRLB |= SERCOM_USART_CTRLB_TXEN_Msk;

    /* Wait for sync */
    while((SERCOM4_REGS->USART.SERCOM_SYNCBUSY) != 0U)
    {
        /* Do nothing */
    }
}

void SERCOM4_USART_TransmitterDisable( void )
{
    SERCOM4_REGS->USART.SERCOM_CTRLB &= ~SERCOM_USART_CTRLB_TXEN_Msk;

    /* Wait for sync */
    while((SERCOM4_REGS->USART.SERCOM_SYNCBUSY) != 0U)
    {
        /* Do nothing */
    }
}

bool SERCOM4_USART_Write( void *buffer, const size_t size )
{
    bool writeStatus      = false;
    uint8_t *pu8Data      = (uint8_t*)buffer;
    uint16_t *pu16Data    = (uint16_t*)buffer;
    uint32_t u32Index     = 0U;

    if(buffer != NULL)
    {
        /* Blocks while buffer is being transferred */
        while(u32Index < size)
        {
            /* Check if USART is ready for new data */
            while((SERCOM4_REGS->USART.SERCOM_INTFLAG & (uint8_t)SERCOM_USART_INTFLAG_DRE_Msk) == 0U)
            {
                /* Do nothing */
            }

            /* Write data to USART module */
            if (((SERCOM4_REGS->USART.SERCOM_CTRLB & SERCOM_USART_CTRLB_CHSIZE_Msk) >> SERCOM_USART_CTRLB_CHSIZE_Pos) != 0x01U)
            {
                /* 8-bit mode */
                SERCOM4_REGS->USART.SERCOM_DATA = pu8Data[u32Index];
            }
            else
            {
                /* 9-bit mode */
                SERCOM4_REGS->USART.SERCOM_DATA = pu16Data[u32Index];
            }

            /* Increment index */
            u32Index++;
        }
        writeStatus = true;
    }

    return writeStatus;
}


bool SERCOM4_USART_TransmitterIsReady( void )
{
    bool transmitterStatus = false;

    if ((SERCOM4_REGS->USART.SERCOM_INTFLAG & SERCOM_USART_INTFLAG_DRE_Msk) == SERCOM_USART_INTFLAG_DRE_Msk)
    {
        transmitterStatus = true;
    }

    return transmitterStatus;
}

void SERCOM4_USART_WriteByte( int data )
{
    /* Check if USART is ready for new data */
    while((SERCOM4_REGS->USART.SERCOM_INTFLAG & SERCOM_USART_INTFLAG_DRE_Msk) == 0U)
    {
        /* Do nothing */
    }

    SERCOM4_REGS->USART.SERCOM_DATA = (uint16_t)data;
}

bool SERCOM4_USART_TransmitComplete( void )
{
    bool transmitComplete = false;

    if ((SERCOM4_REGS->USART.SERCOM_INTFLAG & SERCOM_USART_INTFLAG_TXC_Msk) == SERCOM_USART_INTFLAG_TXC_Msk)
    {
        transmitComplete = true;
    }

    return transmitComplete;
}

void SERCOM4_USART_ReceiverEnable( void )
{
    SERCOM4_REGS->USART.SERCOM_CTRLB |= SERCOM_USART_CTRLB_RXEN_Msk;

    /* Wait for sync */
    while((SERCOM4_REGS->USART.SERCOM_SYNCBUSY) != 0U)
    {
        /* Do nothing */
    }
}

void SERCOM4_USART_ReceiverDisable( void )
{
    SERCOM4_REGS->USART.SERCOM_CTRLB &= ~SERCOM_USART_CTRLB_RXEN_Msk;

    /* Wait for sync */
    while((SERCOM4_REGS->USART.SERCOM_SYNCBUSY) != 0U)
    {
        /* Do nothing */
    }
}

bool SERCOM4_USART_Read( void *buffer, const size_t size )
{
    bool readStatus         = false;
    uint8_t* pu8Data        = (uint8_t*)buffer;
    uint16_t *pu16Data      = (uint16_t*)buffer;
    uint32_t u32Index       = 0U;
    USART_ERROR errorStatus = USART_ERROR_NONE;

    if(buffer != NULL)
    {

        /* Clear error flags and flush out error data that may have been received when no active request was pending */
        SERCOM4_USART_ErrorClear();

        while(u32Index < size)
        {
            /* Check if USART has new data */
            while((SERCOM4_REGS->USART.SERCOM_INTFLAG & SERCOM_USART_INTFLAG_RXC_Msk) == 0U)
            {
                /* Do nothing */
            }

            errorStatus = (USART_ERROR) (SERCOM4_REGS->USART.SERCOM_STATUS & (uint16_t)(SERCOM_USART_STATUS_PERR_Msk | SERCOM_USART_STATUS_FERR_Msk | SERCOM_USART_STATUS_BUFOVF_Msk));

            if(errorStatus != USART_ERROR_NONE)
            {
                break;
            }

            if (((SERCOM4_REGS->USART.SERCOM_CTRLB & SERCOM_USART_CTRLB_CHSIZE_Msk) >> SERCOM_USART_CTRLB_CHSIZE_Pos) != 0x01U)
            {
                /* 8-bit mode */
                pu8Data[u32Index] = (uint8_t)SERCOM4_REGS->USART.SERCOM_DATA;
            }
            else
            {
                /* 9-bit mode */
                pu16Data[u32Index] = (uint16_t)SERCOM4_REGS->USART.SERCOM_DATA;
            }

            /* Increment index */
            u32Index++;
        }

        if(size == u32Index)
        {
            readStatus = true;
        }
    }

    return readStatus;
}

bool SERCOM4_USART_ReceiverIsReady( void )
{
    bool receiverStatus = false;

    if ((SERCOM4_REGS->USART.SERCOM_INTFLAG & SERCOM_USART_INTFLAG_RXC_Msk) == SERCOM_USART_INTFLAG_RXC_Msk)
    {
        receiverStatus = true;
    }

    return receiverStatus;
}

int SERCOM4_USART_ReadByte( void )
{
    return (int)SERCOM4_REGS->USART.SERCOM_DATA;
}





